package com.example.controller;

import com.example.exception.DatabaseConfigurationException;
import com.example.exception.DatabaseConnectionException;
import com.example.exception.SqlExceptionHandler;
import com.example.model.DbConnectionRequest;
import com.example.model.ErrorResponse;
import com.example.service.DbConnectionService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.web.servlet.MockMvc;

import java.sql.SQLException;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(DbController.class)
class DbControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private DbConnectionService dbConnectionService;

    @MockBean
    private SqlExceptionHandler sqlExceptionHandler;

    @Autowired
    private ObjectMapper objectMapper;

    private DbConnectionRequest validRequest;

    @BeforeEach
    void setUp() {
        validRequest = new DbConnectionRequest();
        validRequest.setUrl("jdbc:mysql://localhost:3306/test");
        validRequest.setUsername("root");
        validRequest.setPassword("password");
    }

    @Test
    void testConnectionSuccess() throws Exception {
        // 模拟成功连接
        doNothing().when(dbConnectionService).testConnection(anyString(), anyString(), anyString());

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(validRequest)))
                .andExpect(status().isOk());

        verify(dbConnectionService).testConnection(validRequest.getUrl(), validRequest.getUsername(), validRequest.getPassword());
    }

    @Test
    void testConnectionAuthenticationFailure() throws Exception {
        // 模拟认证失败 (SQLState 28xxx)
        SQLException authException = new SQLException("Access denied for user 'root'@'localhost'", "28000", 1045);
        doThrow(authException).when(dbConnectionService).testConnection(anyString(), anyString(), anyString());

        // 模拟 SqlExceptionHandler 返回配置错误
        ResponseEntity<ErrorResponse> configErrorResponse = ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(new ErrorResponse("DB_CONFIG_ERROR", "数据库配置信息有误，请检查连接参数"));
        when(sqlExceptionHandler.handleSqlException(authException)).thenReturn(configErrorResponse);

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(validRequest)))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.code").value("DB_CONFIG_ERROR"))
                .andExpect(jsonPath("$.message").value("数据库配置信息有误，请检查连接参数"));
    }

    @Test
    void testConnectionHostUnreachable() throws Exception {
        // 模拟主机不可达 (SQLState 08xxx)
        SQLException connectionException = new SQLException("Communications link failure", "08001", 0);
        doThrow(connectionException).when(dbConnectionService).testConnection(anyString(), anyString(), anyString());

        // 模拟 SqlExceptionHandler 返回连接错误
        ResponseEntity<ErrorResponse> connectionErrorResponse = ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                .body(new ErrorResponse("DB_CONNECTION_ERROR", "服务端连接不通，请检查网络连通性"));
        when(sqlExceptionHandler.handleSqlException(connectionException)).thenReturn(connectionErrorResponse);

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(validRequest)))
                .andExpect(status().isServiceUnavailable())
                .andExpect(jsonPath("$.code").value("DB_CONNECTION_ERROR"))
                .andExpect(jsonPath("$.message").value("服务端连接不通，请检查网络连通性"));
    }

    @Test
    void testConnectionAccessDenied() throws Exception {
        // 模拟访问被拒绝 (SQLState 42000)
        SQLException accessException = new SQLException("Access denied for database 'test'", "42000", 1044);
        doThrow(accessException).when(dbConnectionService).testConnection(anyString(), anyString(), anyString());

        // 模拟 SqlExceptionHandler 返回配置错误
        ResponseEntity<ErrorResponse> configErrorResponse = ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(new ErrorResponse("DB_CONFIG_ERROR", "数据库配置信息有误，请检查连接参数"));
        when(sqlExceptionHandler.handleSqlException(accessException)).thenReturn(configErrorResponse);

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(validRequest)))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.code").value("DB_CONFIG_ERROR"))
                .andExpect(jsonPath("$.message").value("数据库配置信息有误，请检查连接参数"));
    }

    @Test
    void testConnectionUnknownDatabase() throws Exception {
        // 模拟数据库不存在
        SQLException dbException = new SQLException("Unknown database 'nonexistent'", "42000", 1049);
        doThrow(dbException).when(dbConnectionService).testConnection(anyString(), anyString(), anyString());

        // 模拟 SqlExceptionHandler 返回配置错误
        ResponseEntity<ErrorResponse> configErrorResponse = ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(new ErrorResponse("DB_CONFIG_ERROR", "数据库配置信息有误，请检查连接参数"));
        when(sqlExceptionHandler.handleSqlException(dbException)).thenReturn(configErrorResponse);

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(validRequest)))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.code").value("DB_CONFIG_ERROR"))
                .andExpect(jsonPath("$.message").value("数据库配置信息有误，请检查连接参数"));
    }

    @Test
    void testConnectionTimeout() throws Exception {
        // 模拟连接超时
        SQLException timeoutException = new SQLException("Connection timed out", "08S01", 0);
        doThrow(timeoutException).when(dbConnectionService).testConnection(anyString(), anyString(), anyString());

        // 模拟 SqlExceptionHandler 返回连接错误
        ResponseEntity<ErrorResponse> connectionErrorResponse = ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                .body(new ErrorResponse("DB_CONNECTION_ERROR", "服务端连接不通，请检查网络连通性"));
        when(sqlExceptionHandler.handleSqlException(timeoutException)).thenReturn(connectionErrorResponse);

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(validRequest)))
                .andExpect(status().isServiceUnavailable())
                .andExpect(jsonPath("$.code").value("DB_CONNECTION_ERROR"))
                .andExpect(jsonPath("$.message").value("服务端连接不通，请检查网络连通性"));
    }

    @Test
    void testConnectionRefused() throws Exception {
        // 模拟连接被拒绝
        SQLException refusedException = new SQLException("Connection refused", "08S01", 0);
        doThrow(refusedException).when(dbConnectionService).testConnection(anyString(), anyString(), anyString());

        // 模拟 SqlExceptionHandler 返回连接错误
        ResponseEntity<ErrorResponse> connectionErrorResponse = ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                .body(new ErrorResponse("DB_CONNECTION_ERROR", "服务端连接不通，请检查网络连通性"));
        when(sqlExceptionHandler.handleSqlException(refusedException)).thenReturn(connectionErrorResponse);

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(validRequest)))
                .andExpect(status().isServiceUnavailable())
                .andExpect(jsonPath("$.code").value("DB_CONNECTION_ERROR"))
                .andExpect(jsonPath("$.message").value("服务端连接不通，请检查网络连通性"));
    }

    @Test
    void testConnectionBadHandshake() throws Exception {
        // 模拟握手失败
        SQLException handshakeException = new SQLException("Bad handshake", "08S01", 0);
        doThrow(handshakeException).when(dbConnectionService).testConnection(anyString(), anyString(), anyString());

        // 模拟 SqlExceptionHandler 返回配置错误
        ResponseEntity<ErrorResponse> configErrorResponse = ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(new ErrorResponse("DB_CONFIG_ERROR", "数据库配置信息有误，请检查连接参数"));
        when(sqlExceptionHandler.handleSqlException(handshakeException)).thenReturn(configErrorResponse);

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(validRequest)))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.code").value("DB_CONFIG_ERROR"))
                .andExpect(jsonPath("$.message").value("数据库配置信息有误，请检查连接参数"));
    }

    @Test
    void testConnectionGenericError() throws Exception {
        // 模拟通用错误
        SQLException genericException = new SQLException("Some unknown error occurred", "HY000", 0);
        doThrow(genericException).when(dbConnectionService).testConnection(anyString(), anyString(), anyString());

        // 模拟 SqlExceptionHandler 返回连接错误
        ResponseEntity<ErrorResponse> connectionErrorResponse = ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                .body(new ErrorResponse("DB_CONNECTION_ERROR", "服务端连接不通，请检查网络连通性"));
        when(sqlExceptionHandler.handleSqlException(genericException)).thenReturn(connectionErrorResponse);

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(validRequest)))
                .andExpect(status().isServiceUnavailable())
                .andExpect(jsonPath("$.code").value("DB_CONNECTION_ERROR"))
                .andExpect(jsonPath("$.message").value("服务端连接不通，请检查网络连通性"));
    }

    @Test
    void testInvalidRequestValidation() throws Exception {
        // 测试请求参数验证
        DbConnectionRequest invalidRequest = new DbConnectionRequest();
        invalidRequest.setUrl(""); // 空URL
        invalidRequest.setUsername(""); // 空用户名
        invalidRequest.setPassword(""); // 空密码

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(invalidRequest)))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.code").value("VALIDATION_ERROR"))
                .andExpect(jsonPath("$.message").value("请求参数格式不正确"));
    }

    @Test
    void testMissingRequestFields() throws Exception {
        // 测试缺少字段的请求
        String incompleteJson = "{\"url\":\"jdbc:mysql://localhost:3306/test\"}";

        mockMvc.perform(post("/db/test-connection")
                .contentType(MediaType.APPLICATION_JSON)
                .content(incompleteJson))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.code").value("VALIDATION_ERROR"))
                .andExpect(jsonPath("$.message").value("请求参数格式不正确"));
    }
} 